<?php

namespace app\common\service;

use think\Db;
use think\Exception;
use app\common\model\ads\Ad as AdModel;
use app\common\model\ads\GoldLog;
use app\common\model\MoneyLog;
use app\common\model\User;

/**
 * 红包
 */
class Redpack
{

    /**
     * 领取广告赏金
     */
    public static function getGold($ad_id, $user_id){
        if ($ad_id == 0) {
            throw new Exception("广告参数有误");
        }
        $where_ad['id'] = $ad_id;
        $where_ad['status'] = 1;
        $where_ad['display'] = 1;
        $ad_info = AdModel::where($where_ad)->find();
        if (!$ad_info) {
            throw new Exception("广告不存在或已下架");
        }
        if ($ad_info['type'] != 1) {
            throw new Exception("该广告不存在奖励");
        }

        if ($ad_info['effecttime'] < time()) {
            throw new Exception("赏金已失效");
        }

        //是否已领取过
        if (self::isGotGold($user_id, $ad_id)) {
            throw new Exception("您已领取过");
        }
        
        // max = max_jinbi_get_perday
        $maxGotPerDay = config('site.max_jinbi_get_perday');

        if($maxGotPerDay > 0){
            $today = GoldLog::where([
                'user_id' => $user_id,
                'updatetime' => ['egt',strtotime(date('Y-m-d'))],
            ])->count();

            if($today && $today >= $maxGotPerDay){
                throw new Exception("今日领取次数已达上限");
            }
        }

        Db::startTrans();

        $log = GoldLog::where([
            'status' => '0',
            'ad_id' => $ad_id,
        ])->find();
        
        if(!$log){
            Db::rollback();
            throw new Exception("已经被领完了");
        }
        //修改用户金币 添加变动记录
        $update_user_gold = User::money($log->gold_num, $user_id, '观看赏金广告', 0, MoneyLog::TYPE_VIEW_AD);
        //修改广告已经领取过的赏金
        $update_ad = AdModel::where(array('id' => $ad_id))->setInc('get_num', $log->gold_num);
        //修改赏金状态
        $log->status = '1';
        $log->user_id = $user_id;
        $update_log = $log->save();

        if ($update_user_gold === false || $update_ad === false || $update_log === false) {
            Db::rollback();
            throw new Exception("领取失败:01");
        }

        #给上级返奖
        $reward = \app\common\model\huntermanager\Hunter::rewardForAdView($log);

        Db::commit();
        return $log->gold_num;
    }

    /**
     * 获取领取人列表[简易的]
     * @param int $user_id 如果传值，则必须包含该用户
     */
    public static function getGotters( $ad_id, $num = 5, $user_id = null){
        //是否已领取过
        $model = GoldLog::where('ad_id', $ad_id);

        // $res['count'] = $model->count();

        if($user_id){
            $model = $model->whereOr(function($query) use($ad_id, $user_id){
                $query->where([
                    'ad_id' => $ad_id,
                    'user_id' => $user_id,
                ]);
            });
        }
        $list = $model->where('user_id','>', 0)->limit($num)->fetchSql(0)->select();

        foreach ($list as $key => &$value) {
            $value['user_info'] = User::getBaseInfoNosense($value['user_id']);
        }

        // $res['list'] = $list;

        return $list;
    }

    /**
     * 判断用户是否领取过
     */
    public static function isGotGold($user_id, $ad_id){
        //是否已领取过
        $is_get = Db::name('self_gold_log')->where(array('user_id' => $user_id, 'ad_id' => $ad_id))->count();
        if ($is_get > 0) {
            return true;
        }
        return false;
    }

    /**
     * 生成红包【规则：扣除服务费之后再生成】
     * @param string $amount 总金额
     * @param int $count 总个数
     */
    public static function generatorRedpack($adId = 0, $amount, $count = 1)
    {

        // 扣除完服务费
        $amount = bcsub($amount, Amount::getAdSerFee($amount), 2);

        $luck_bate = config('site.jinbi_rule'); //运气红包百分比
        $rmb_rate = config('site.rmb_rate');    //金币，rmb兑换比


        $gold_amount = floor($amount * $rmb_rate);    //可分发的金币总数

        $luck_bate = ($luck_bate ? $luck_bate : 0) / 100; // 随机红包的比例【金额和数量】

        $luck_num = floor($count * $luck_bate); //运气红包数量
        $pre_num = $count - $luck_num; //等额红包数量

        $luck_amount = floor($gold_amount * $luck_bate); //运气红包总金币数
        $pre_amount = floor($gold_amount - $luck_amount); //等额红包总金币数

        #校验补差
        $total = $luck_amount + $pre_amount;
        $dirt = $total - $gold_amount;
        if($dirt != 0){
            // 超出了 或者 不够
            $luck_amount = $luck_amount - $dirt;
        }
        
        #开始对等额的生成数据
        $pre_arr = [];
        if($pre_num > 0){
            $pre_money = floor($pre_amount / $pre_num); //等额红包的金额
            if($pre_money < 1){
                throw new Exception("奖励无法满足最小分配值：01");
            }
            for ( $i = 0; $i < $pre_num; $i++) { 
                $pre_arr[] = $pre_money;
            }
        }

        $luck_arr = self::buildLuckPackLimit($luck_num, $luck_amount);
    
        #合并
        $redpacks = array_merge($pre_arr, $luck_arr);

        #总额校验
        $total_gold = 0;
        $insertData = [];
        foreach ($redpacks as $key => $value) {
            $total_gold += $value;
            $insertData[] = [
                'ad_id' => $adId,
                'gold_num' => $value,
                'createtime' => time(),
            ];
        }

        if($total_gold != $gold_amount){
            throw new Exception("总额不对：{$luck_num}--{$pre_num}--{$total_gold}--{$gold_amount}--{$pre_amount}--{$luck_amount}");
        }

        #随机打乱
        shuffle($redpacks); 
        // return ([
        //     'luck_arr' => $luck_arr,
        //     'luck_num' => $luck_num,
        //     'real_luck_num' => count($luck_arr),
        //     'pre_arr' => $pre_arr,
        //     'pre_num' => $pre_num,
        //     'real_pre_num' => count($pre_arr),
        //     'real_pack_num' => count($redpacks),
        // ]);
        $insert = (new GoldLog)->insertAll($insertData);
        if( $insert ){
            AdModel::where('id', $adId)->update([
                'gold_num' => $gold_amount
            ]);
        }
        return $insert;
    }



    /**
     * 生成运气红包
     * @param $num 生成总数量
     * @param $gold_num 总金币数量
     */
    public static function buildLuckPack($num, $gold_num)
    {
        $pre_money = floor($gold_num / $num); //均分下来红包的金额

        if($pre_money < 1){
            throw new Exception("奖励无法满足最小分配值：02");
        }

        #生成办法为，生成 N-1 个不重复的随机数，且数值小于 最大值，此随机数将作为切割器

        $count = 0;
        $split_dot = [];
        $max =  floor($gold_num - 1);
        while ($count < $num - 1) {
            $split_dot[] = mt_rand(1, $max);
            $split_dot = array_flip(array_flip($split_dot));
            $count = count($split_dot);
        }

        #按大小升序排序
        asort($split_dot);

        $res = [];
        $pre = 0;
        $split_dot[] = floor($gold_num);
        foreach ($split_dot as $key => $v) {
            $res[] = $v - $pre;
            $pre = $v;
        }
        
        unset($count, $split_dot, $max, $pre, $key, $v);

        return $res;
    }

    /**
     * 生成运气红包
     * @param $num 生成总数量
     * @param $gold_num 总金币数量
     */
    public static function buildLuckPackLimit($num, $gold_num)
    {
        if($num < 1) return [];

        #随机红包最大额所占随机总额比例
        $permax = config('site.jinbi_rule_max');


        if(!$permax || $permax <= 0 || $permax >= 100){
            return self::buildLuckPack($num, $gold_num);
        }

        $permax = $gold_num * $permax / 100;

        $pre_money = floor($gold_num / $num); //均分下来红包的金额

        if($pre_money < 1){
            throw new Exception("奖励无法满足最小分配值：02");
        }

        $pre_money = round($pre_money);

        #如果最大值小于等于平均值，将按照平均值分配
        if($permax <= $pre_money){
            $res = [];
            $total = 0;
            for ($i = 0; $i < $num; $i++) { 
                $res[] = $pre_money;
                $total += $pre_money;
            }
            $dirt = $gold_num - $total;
            if($dirt != 0){
                $mul = $dirt > 0 ? 1 : -1;
                for ($n = 0; $n < abs($dirt); $n++) { 
                    $res[$n] += $mul;
                }
            }
            return $res;
        }

        #生成办法为，生成 N-1 个不重复的随机数，且数值小于 最大值，此随机数将作为切割器

        $count = 0;
        $split_dot = [];
        $max =  floor($gold_num - 1);
        while ($count < $num - 1) {
            $split_dot[] = mt_rand(1, $max);
            $split_dot = array_flip(array_flip($split_dot));
            $count = count($split_dot);
        }

        #按大小升序排序
        asort($split_dot);

        $res = [];
        $pre = 0;
        $split_dot[] = floor($gold_num);
        #超出的量
        $more_total = 0;
        foreach ($split_dot as $key => $v) {
            $cval = $v - $pre;
            $dirt_cv = $cval - $permax;
            if($dirt_cv > 0){
                $res[] = $permax;
                $more_total += $dirt_cv;
            }else{
                $res[] = $cval;
            }
            
            $pre = $v;
        }
        
        unset($count, $split_dot, $dirt_cv, $max, $cval, $pre, $key, $v);
        if($more_total > 0){
            #出现了超限了的情况，逐个进补
            foreach ($res as $key => &$item) {
                if($more_total > 0 ){
                    $dirt_cv = $permax - $item;
                    if($dirt_cv > 0){
                        #进补值
                        $dval = $more_total > $permax ? $dirt_cv : $more_total;
                        $item += $dval;
                        $more_total -= $dval;
                    }
                }
            }
        }
        return $res;
    }
}
